#ifndef CVX_V_VAMERA_H
#define CVX_V_VAMERA_H 1

/*
	off line calibration method
	generally, the input is a sequence of images or their corner position and output is camera intrinsic 
	parameters.
*/

#include "cvxImage.h"
#include "cvxBoard.h"
#include <vector>

using std::vector;

/************************************************************************/
/* 
	CvxVCamera: base class of cameras for calibration
*/
/************************************************************************/
class CvxVCamera	
{
public:
	CvxVCamera();
	virtual ~CvxVCamera();

	/************************************************************************/
	/* 
	flags: calibration flags, support
			#define CV_CALIB_FIX_ASPECT_RATIO     2
			#define CV_CALIB_FIX_PRINCIPAL_POINT  4
			#define CV_CALIB_ZERO_TANGENT_DIST    8
			#define CV_CALIB_FIX_K1  32
			#define CV_CALIB_FIX_K2  64
			#define CV_CALIB_FIX_K3  128
			#define CV_CALIB_FIX_K4  2048
			#define CV_CALIB_FIX_K5  4096
			#define CV_CALIB_FIX_K6  8192
			and their combinations
	imageSize: calibration image size 
	*/
	/************************************************************************/
	bool config(int flags, const CvSize &imageSize);

	/************************************************************************/
	/* 
	ptsVec: corner positions in images
	targetPts: physical position of target (checker board)
	images: optional, calibration images
	pBoard: optional, calibration chessboard parameters
	*/
	/************************************************************************/
	virtual bool setCalibData(const vector<vector<Point2f>> &ptsVec,  const vector<Point3f> &targetPts, 
							  const vector<IplImage *> &images, const CvxBoard *pBoard = NULL) = 0;

	virtual bool calib() = 0;
	const Mat & getIntrinsic(void);
	const Mat & getDistortion(void);
	const vector<Mat> & getRotationVecs(void);
	const vector<Mat> & getRotationMatrixes(void);
	const vector<Mat> & getTranslationVecs(void);
	void saveIntrinsic(const string &filename)const;

protected:
	CvSize m_imageSize;
	int m_flags;                //calibration flags

	vector<Point3f> m_objPts;              //all frames objects position
	vector<vector<Point2f> > m_imgPts;     //all frames image corner position

	//intrinsic parameters
	Mat m_intrinsic;	//3*3 camera matrix	
	Mat m_distortion;   // 5*1 distortion vector  k1, k2, p1, p2, k3	
	//extrinsic parameters
	vector<Mat> m_rvecs;    //Rodrigues rotation vector
	vector<Mat> m_rMats;    //rotate matrix 3 * 3
	vector<Mat> m_tvecs;    //3*1

	double	m_aspectRatio; // fx/fy
	Mat m_parameterErr;    //focal length 2, principle point 2, distortion 4;
	Mat m_stdDev;          //standard deviation of reprojection error
	double m_rms;          //root mean square error, reprojection error.
	
	//physical 
	double m_apertureWidth;        //1/3" type 4.8mm
	double m_apertureHeight;       //          3.6mm
	double m_focalLength;          //unit mm
	Point2d m_principalPoint;      //unit pixel
	double m_fovx;  //field of view in degrees along the horizontal sensor axis
	double m_fovy;  //field of view in degrees along the vertical sensor axis

	//parameters for easy debug
	double *_fx, *_fy, *_cx, *_cy, *_k1, *_k2, *_p1, *_p2, *_k3;

private:
	void initCamera();

	/*
		calibration interface from opencv
	*/
protected:
	static bool calibration(const vector<vector<cv::Point3f> > &objectPoints, 
							const vector<vector<cv::Point2f> > &imagePoints,
							const CvSize imageSize, const int flags, Mat& cameraMatrix, Mat& distCoeffs,
							vector<Mat>& rvecs, vector<Mat>& tvecs, double &rms);
};


/************************************************************************/
/* 
	CvxOpenCvCamera: use calibration method in open CV
	Generally, it is faster than other optimized method.
*/
/************************************************************************/

class CvxOpenCvCamera :public CvxVCamera
{
public:
	CvxOpenCvCamera();
	~CvxOpenCvCamera();

	bool setCalibData(const vector<vector<Point2f>> &ptsVec,  const vector<Point3f> &targetPts, 
					  const vector<IplImage *> &images, const CvxBoard *pBoard = NULL);
	bool calib();
};

/************************************************************************/
/* 
	CvxDKKCamera from "Accurate Camera Calibration using Iterative Refinement of Control Points"
	by A.Datta, J.S.K and T.Kanade
	Generally, it is slower but accurate.
*/
/************************************************************************/

class CvxDKKCamera :public CvxVCamera
{
public:
	CvxDKKCamera();
	~CvxDKKCamera();

	bool setCalibData(const vector<vector<Point2f>> &ptsVec,  const vector<Point3f> &targetPts, 
					  const vector<IplImage *> &images, const CvxBoard *pBoard = NULL);
	bool calib();

private:
	vector<IplImage *> m_pImages;           // point to images
	CvxBoard * m_pBoard;                    // point to board

};

/************************************************************************/
/* 
	bundle adjust camera from "Robust camera calibration using inaccurate targets"
	A. Albarellli, E. Rodola, A. Torsello
*/
/************************************************************************/
class CvxBACamera :public CvxVCamera
{
public:
	CvxBACamera();
	~CvxBACamera();
	
};



#endif